use crate::utils::constant::{get_config_path, get_packages_path};
use crate::utils::local_data::{get_local_value, set_local_value, ProjectInfo};
use crate::utils::util::{
    copy_dir_all, create_dir_with_complete_path, is_file, read_toolchain_config,
};
use io::Error;
use log::{error, info};
use snowflake::SnowflakeIdGenerator;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::{fs, io};

struct TemplateModifier {
    relative_path: String,
    kv_list: Vec<HashMap<String, String>>,
}

impl TemplateModifier {
    fn new(relative_path: String, kv_list: Vec<HashMap<String, String>>) -> TemplateModifier {
        TemplateModifier {
            relative_path,
            kv_list,
        }
    }
}
pub fn project_init(
    name: String,
    project_type: String,
    os: String,
    ide: Option<String>,
    dir: Option<String>,
    lang: Option<String>,
    arch: Option<String>,
) {
    let project_dir_path = dir.unwrap();
    let workspace_dir = Path::new(&project_dir_path);
    let project_path = workspace_dir.join(&name.clone());
    let template_dir = get_config_path();
    let target_template_path = template_dir
        .join("template")
        .join("project")
        .join(&ide.clone().unwrap())
        .join(&os)
        .join(&arch.clone().unwrap())
        .join(&lang.clone().unwrap())
        .join(&project_type);
    if !project_path.exists() {
        match create_dir_with_complete_path(project_path.to_str().unwrap()) {
            Ok(_) => {}
            Err(e) => {
                error!("项目创建失败: {}", e);
                return;
            }
        }
    } else {
        error!("项目已存在");
        return;
    }
    match copy_dir_all(target_template_path, project_path.clone()) {
        Ok(_) => {}
        Err(e) => {
            error!("项目初始化失败: {}", e);
            return;
        }
    }
    let mut modified_list: TemplateModifier = TemplateModifier::new("".to_string(), vec![]);
    if ide.unwrap() == "eclipse" {
        modified_list.relative_path = ".project".to_string();
        modified_list.kv_list = vec![HashMap::from([
            ("key".to_string(), "ProjectName".to_string()),
            ("value".to_string(), name.clone()),
        ])];
    } else {
        let toolchain_config = get_toolchain_config(&arch.clone().unwrap(), &os).unwrap();
        let tc_version = toolchain_config.get("version").unwrap();
        modified_list.relative_path = String::from("vs-config/vs-config.json");
        let mut kv_list = Vec::new();
        kv_list.push(HashMap::from([
            ("key".to_string(), "ProjectName".to_string()),
            ("value".to_string(), name.clone()),
        ]));
        kv_list.push(HashMap::from([
            ("key".to_string(), "lang".to_string()),
            (
                "value".to_string(),
                convert_vs_config_lang(&*lang.clone().unwrap()),
            ),
        ]));
        kv_list.push(HashMap::from([
            ("key".to_string(), "ProjectType".to_string()),
            ("value".to_string(), project_type.clone()),
        ]));
        kv_list.push(HashMap::from([
            ("key".to_string(), "ProjectPath".to_string()),
            (
                "value".to_string(),
                project_path.clone().to_str().unwrap().to_string(),
            ),
        ]));
        kv_list.push(HashMap::from([
            ("key".to_string(), "arch".to_string()),
            ("value".to_string(), arch.clone().unwrap()),
        ]));
        kv_list.push(HashMap::from([
            ("key".to_string(), "tc_version".to_string()),
            ("value".to_string(), tc_version.to_string()),
        ]));
        modified_list.kv_list = kv_list;
    }
    template_modify(modified_list, project_path.as_path()).unwrap();
    let old_code_workspace = project_path.join("$ProjectName.code-workspace");
    let new_code_workspace =
        project_path.join("$ProjectName.code-workspace".replace("$ProjectName", &name.clone()));
    fs::rename(old_code_workspace, new_code_workspace).unwrap();
    info!("项目初始化成功");
    handle_local_info(
        name,
        project_type,
        os,
        project_path.to_str().unwrap().to_string(),
        lang.clone().unwrap(),
        arch.clone().unwrap(),
    )
}

fn handle_local_info(
    name: String,
    project_type: String,
    os: String,
    dir: String,
    lang: String,
    arch: String,
) {
    let mut local_info = get_local_value().unwrap();
    let mut project_list = local_info.project_list.clone();
    let mut snowflake = SnowflakeIdGenerator::new(1, 1);
    project_list.push(ProjectInfo {
        id: snowflake.generate().to_string(),
        name,
        project_type,
        lang,
        os_type: os,
        arch,
        path: dir,
        is_compiled: false,
    });
    local_info.project_list = project_list;
    set_local_value(local_info).expect("TODO: panic message");
}

fn template_modify(modified_list: TemplateModifier, target_project_path: &Path) -> io::Result<()> {
    let relative_path = modified_list.relative_path;
    let template_absolute_path = target_project_path.join(relative_path);
    if !is_file(template_absolute_path.to_str().unwrap()) {
        error!(
            "非法文件 {}",
            template_absolute_path.clone().to_str().unwrap()
        );
        return Err(Error::new(io::ErrorKind::Other, "非法文件"));
    }
    let mut file = File::options()
        .read(true)
        .write(true)
        .open(template_absolute_path)?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    file.seek(SeekFrom::Start(0))?;
    file.set_len(0)?;
    for kv in modified_list.kv_list.iter() {
        let replace_str = "{{".to_string() + kv.get("key").unwrap() + "}}";
        content = content.replace(&replace_str, kv.get("value").unwrap());
    }
    file.write_all(content.as_bytes())?;
    Ok(())
}
///转换为vs-config.json可识别的lang参数
fn convert_vs_config_lang(origin_value: &str) -> String {
    let mut target_value = origin_value.to_string();
    if target_value == "cpp" {
        target_value = "C++".to_string();
    }
    target_value
}

fn get_toolchain_config(arch: &str, os: &str) -> io::Result<HashMap<String, String>> {
    let package_base = get_packages_path();
    let toolchain_cfg_path = package_base.join("toolchain_gcc").join("toolchain.xml");
    if !is_file(toolchain_cfg_path.to_str().unwrap()) {
        error!(
            "{} 工具链配置路径不存在",
            toolchain_cfg_path.to_str().unwrap()
        )
    }
    let result = read_toolchain_config(toolchain_cfg_path.to_str().unwrap())?;
    for item in result.iter() {
        if item.get("arch").unwrap() == arch && item.get("os_type").unwrap() == os {
            return Ok(item.clone());
        }
    }
    Err(Error::new(io::ErrorKind::Other, "未找到匹配的工具链配置"))
}
